/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.model.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.createUniqueName;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getMessages;

import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.MessageEnd;
import org.eclipse.uml2.uml.MessageEvent;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
import org.eclipse.uml2.uml.MessageSort;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.ReceiveOperationEvent;
import org.eclipse.uml2.uml.ReceiveSignalEvent;
import org.eclipse.uml2.uml.SendOperationEvent;
import org.eclipse.uml2.uml.SendSignalEvent;
import org.eclipse.uml2.uml.UMLFactory;

/**
 * Command to create Message elements in the UML model.
 * <p>
 * The following attributes must be set for the command to complete
 * successfully:
 * </p>
 * <ul>
 * <li>SendPosition
 * <li>ReceivePosition
 * <li>MessageSort
 * </ul>
 * <p>
 * The following attributes can be read to access the result of the command:
 * </p>
 * <ul>
 * <li>Message
 * </ul>
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class CreateMessageCommand extends AbstractMessageCommand {

	private MessageSort messageSort;
	private Message message;

	private MessageEnd sendMsgEnd;
	private MessageEnd recvMsgEnd;
	
	private MessageEvent sendEvent;
	private MessageEvent recvEvent;

	public void setMessageSort(final MessageSort sort) {
		this.messageSort = sort;
	}

	public Message getMessage() {
		return message;
	}

	@Override
	public void execute() {
		sendMsgEnd = createMessageEnd(sendPosition, true);
		recvMsgEnd = createMessageEnd(receivePosition, false);

		final Interaction interaction = getInteraction(sendPosition);
		message = UMLFactory.eINSTANCE.createMessage();
		message.setName(createUniqueName(interaction, "Message"));
		message.setMessageSort(messageSort);
		message.setSendEvent(sendMsgEnd);
		message.setReceiveEvent(recvMsgEnd);

		sendEvent = createSendEvent(messageSort, sendMsgEnd, interaction.getNearestPackage());
		recvEvent = createRecvEvent(messageSort, recvMsgEnd, interaction.getNearestPackage());
		
		// Ordinarily, EMF maintains both sides of a bidirectional
		// relationship. It t does not do so, however, for
		// MessageOccurrenceSpecifications in UML 2.1.
		sendMsgEnd.setMessage(message);
		recvMsgEnd.setMessage(message);
		
		getMessages(interaction).add(message);
	}

	@Override
	public void undo() {
		
		message.getNearestPackage().getPackagedElements().remove(sendEvent);
		message.getNearestPackage().getPackagedElements().remove(recvEvent);
		
		message.setInteraction(null);

		removeMessageEnd(sendMsgEnd);
		removeMessageEnd(recvMsgEnd);
	}

	@Override
	public void redo() {
		addMessageEnd(sendPosition, sendMsgEnd, true);
		addMessageEnd(receivePosition, recvMsgEnd, false);
		final Interaction interaction = getInteraction(sendPosition);
		getMessages(interaction).add(message);
		
		if (sendEvent != null) {
			message.getNearestPackage().getPackagedElements().add(sendEvent);
		}
		
		if (recvEvent != null) {
			message.getNearestPackage().getPackagedElements().add(recvEvent);
		}
	}

	@Override
	public void dispose() {
		message = null;
		messageSort = null;
		super.dispose();
	}
	
	/**
	 * Create {@link SendOperationEvent} or {@link SendSignalEvent} depending on {@link MessageSort} value
	 * @param sort of the {@link Message}
	 * @param messageEnd the {@link MessageEnd} on which the event is referenced
	 * @param owner of the new {@link MessageEvent}
	 * @return the created {@link MessageEvent}
	 */
	private MessageEvent createSendEvent(MessageSort sort, MessageEnd messageEnd, Package owner) {
		
		// If MessageEnd is a Gate, no attached Event
		if (! (messageEnd instanceof MessageOccurrenceSpecification)) {
			return null;
		}
		// Create event
		MessageEvent send = null;
		if (sort.equals(MessageSort.ASYNCH_SIGNAL_LITERAL)) {
			// Create Start ExecutionEvent
			send = UMLFactory.eINSTANCE.createSendSignalEvent();
			send.setName(
					createUniqueName(
							owner,
							"SendEvt"));

			
		} else {
			// Create Start ExecutionEvent
			send = UMLFactory.eINSTANCE.createSendOperationEvent();
			send.setName(
					createUniqueName(
							owner,
							"SendEvt"));

		}
		
		// Set event 
		((MessageOccurrenceSpecification) messageEnd).setEvent(send);
		
		// Set owner
		owner.getPackagedElements().add(send);
		
		return send;
	}
	
	/**
	 * Create {@link ReceiveOperationEvent} or {@link ReceiveSignalEvent} depending on {@link MessageSort} value
	 * @param sort of the {@link Message}
	 * @param messageEnd the {@link MessageEnd} on which the event is referenced
	 * @param owner of the new {@link MessageEvent}
	 * @return the created {@link MessageEvent}
	 */
	private MessageEvent createRecvEvent(MessageSort sort, MessageEnd messageEnd, Package owner) {
		
		// If MessageEnd is a Gate, no attached Event
		if (! (messageEnd instanceof MessageOccurrenceSpecification)) {
			return null;
		}
		// Create event
		MessageEvent recv = null;
		if (sort.equals(MessageSort.ASYNCH_SIGNAL_LITERAL)) {
			// Create Start ExecutionEvent
			recv = UMLFactory.eINSTANCE.createReceiveSignalEvent();
			recv.setName(
					createUniqueName(
							owner,
							"RecvEvt"));

			
		} else {
			// Create Start ExecutionEvent
			recv = UMLFactory.eINSTANCE.createReceiveOperationEvent();
			recv.setName(
					createUniqueName(
							owner,
							"RecvEvt"));

		}
		
		// Set event 
		((MessageOccurrenceSpecification) messageEnd).setEvent(recv);
		
		// Set owner
		owner.getPackagedElements().add(recv);
		
		return recv;
	}
}
